package it.getta.vicinometro.places;

import it.getta.vicinometro.model.PlaceDetailGalleryResult;
import it.getta.vicinometro.model.PlaceDetailResult;
import it.getta.vicinometro.model.PlaceList;
import it.getta.vicinometro.resources.ResBundle;
import it.getta.vicinometro.utils.Utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import com.google.android.maps.GeoPoint;
import com.google.api.client.googleapis.GoogleHeaders;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.http.json.JsonHttpParser;
import com.google.api.client.json.jackson.JacksonFactory;

public class NetworkManager {


//	private static final boolean PRINT_AS_STRING = false;
//	private static final int BUFFER_SIZE = 16384;
	
	private static NetworkManager mInstance;
	private ExecutorService mExecutor;

	public static NetworkManager getInstance() {
		if (mInstance == null) {
			mInstance = new NetworkManager();
		}
		return mInstance;
	}

	private NetworkManager() {
		mExecutor = Executors.newFixedThreadPool(3, new ThreadFactory() {

			public Thread newThread(Runnable r) {
				Thread t = new Thread(r);
				t.setPriority(Thread.NORM_PRIORITY);
				return t;
			}
		});
	}

	private abstract class NetworkTask implements Runnable {

		protected NetworkListener mListener;

		public NetworkTask(NetworkListener listener) {
			mListener = listener;
		}
		
		protected final HttpTransport transport = new ApacheHttpTransport();

		public HttpRequestFactory createRequestFactory(
				final HttpTransport transport) {

			return transport.createRequestFactory(new HttpRequestInitializer() {
				public void initialize(HttpRequest request) {
					GoogleHeaders headers = new GoogleHeaders();
					headers.setApplicationName("Google-Places-DemoApp");
					request.setHeaders(headers);
					JsonHttpParser parser = new JsonHttpParser(
							new JacksonFactory());
					request.addParser(parser);
				}
			});
		}

		public void run() {
			runTask();
		}

		public abstract void runTask();
	}

	private class PlaceSearchTask extends NetworkTask {

		private GeoPoint mPoint;
		
		public PlaceSearchTask(final GeoPoint point, NetworkListener listener) {
			super(listener);
			mPoint = point;
		}

		@Override
		public void runTask() {
			try {
				Utils.hardLong("PlaceSearchTask...");
				HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
				HttpRequest request = httpRequestFactory
						.buildGetRequest(new GenericUrl(ResBundle.PLACES_SEARCH_URL));
				
				double lat = mPoint.getLatitudeE6()/1e6;
				double lon = mPoint.getLongitudeE6()/1e6;
				
				request.getUrl().put("key", ResBundle.API_KEY_GOOGLE_PLACES);
				request.getUrl().put("location", lat + "," + lon);
				request.getUrl().put("radius", 500);
				request.getUrl().put("sensor", "false");

				HttpResponse response = request.execute();
				
				PlaceList places = response.parseAs(
						PlaceList.class);
				Utils.hardLong("STATUS = " + response);
				
				if(response.getStatusCode() == 200) {
					mListener.getPlacesSearchPerformed(places);
				} else {
					mListener.getPlacesSearchFailed(Integer.parseInt(places.getStatus()));
				}

			} catch (HttpResponseException e) {
				mListener.getPlacesSearchFailed(e);
				
			} catch (IOException e) {
				mListener.getPlacesSearchFailed(e);
			}
		}

	}
	
	private class PlaceDetailTask extends NetworkTask {

		private String mReference;
		
		public PlaceDetailTask(final String reference, NetworkListener listener) {
			super(listener);
			mReference = reference;
		}

		@Override
		public void runTask() {
			try {
				Utils.hardLong("PlaceDetailTask...");
				Utils.hardLong("reference: " + mReference);
				HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
				HttpRequest request = httpRequestFactory
						.buildGetRequest(new GenericUrl(ResBundle.PLACES_DETAIL_URL));
				
				request.getUrl().put("key", ResBundle.API_KEY_GOOGLE_PLACES);
				request.getUrl().put("reference", mReference);
				request.getUrl().put("sensor", "false");

				HttpResponse response = request.execute();
				
				PlaceDetailResult placeDetailResult = response.parseAs( 
						PlaceDetailResult.class);
				Utils.hardLong("STATUS = " + response.getStatusCode());
				
				
				if(response.getStatusCode() == 200) {
					mListener.getPlacesDetailPerformed(placeDetailResult);
				} else {
					mListener.getPlacesDetailFailed(Integer.parseInt(placeDetailResult.getStatus()));
				}

			} catch (HttpResponseException hre) {
				mListener.getPlacesDetailFailed(hre);
				
			} catch (IOException ioe) {
				mListener.getPlacesDetailFailed(ioe);
			}
		}

	}
	
	private class PlaceGalleryTask extends NetworkTask {

		private String mText;
		private String mLat;
		private String mLon;
		
		public PlaceGalleryTask(final String text, String lat, String lon, NetworkListener listener) {
			super(listener);
			mText = text.trim().replace(' ', '+');
			mLat = lat;
			mLon = lon;
		}

		@Override
		public void runTask() {
			try {
				Utils.hardLong("PlaceGalleryTask...");
				HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
				String rawUrl = ResBundle.PLACES_GALLERY_URL;
				String url = rawUrl.replace("{search_text}", mText);
				url = url.replace("{search_lat}", mLat);
				url = url.replace("{search_lon}", mLon);
				System.out.println("URL: " + url);
				HttpRequest request = httpRequestFactory
						.buildGetRequest(new GenericUrl(url));
				
				HttpResponse response = request.execute();
				
				PlaceDetailGalleryResult placeDetailGalleryResult = response.parseAs(
						PlaceDetailGalleryResult.class);
				
				Utils.hardLong("STATUS = " + placeDetailGalleryResult.getStatus());
				
				if(response.getStatusCode() == 200) {
					if(placeDetailGalleryResult.getPhotos().getPhoto().length > 0) {
						mListener.getPlacesDetailGalleryPerformed(placeDetailGalleryResult);
					} else {
						mListener.getPlacesDetailGalleryNoImages();
					}
				} else {
					mListener.getPlacesDetailGalleryFailed(Integer.parseInt(placeDetailGalleryResult.getStatus()));
				}

			} catch (HttpResponseException hre) {
				mListener.getPlacesDetailGalleryFailed(hre);
				
			} catch (IOException ioe) {
				mListener.getPlacesDetailGalleryFailed(ioe);
			}
		}

	}
	
	public void getPlacesSearchTask(GeoPoint point, NetworkListener listener) {
		PlaceSearchTask task = new PlaceSearchTask(point, listener);
		mExecutor.execute(task);
	}
	
	public void getPlaceDetailTask(String reference, NetworkListener listener) {
		PlaceDetailTask task = new PlaceDetailTask(reference, listener);
		mExecutor.execute(task);
	}
	
	public void getPlaceDetailGalleryTask(String text, String lat, String lon, NetworkListener listener) {
		PlaceGalleryTask task = new PlaceGalleryTask(text, lat, lon, listener) ;
		mExecutor.execute(task);
	}
	
	public String convertStreamToString(InputStream is) throws IOException {
		if (is != null) {
			Writer writer = new StringWriter();

			char[] buffer = new char[1024];
			try {
				Reader reader = new BufferedReader(new InputStreamReader(is,
						"UTF-8"));
				int n;
				while ((n = reader.read(buffer)) != -1) {
					writer.write(buffer, 0, n);
				}
			} finally {
				is.close();
			}
			return writer.toString();
		} else {
			return "";
		}
	}

}
